// 访问临界区的安全与不安全测试
// ./exam_spin_test [m] [flag]
#include "thread.h"
#include "sem.h"

#include <stdio.h>
#include <stdlib.h>
#include <assert.h>

#define NBUMP 30000

static int
unsafe(void *arg)
{
    int *ip = arg;
    for (int i = 0; i < NBUMP; i++) {
        int n = *ip;
        n = n + 1;
        Thread_pause(); // Simulate preempting
        n = n + 1;
        Thread_pause();
        *ip = n + 1;
    }
    return 0;
}

static void
test_unsafe(int m)
{
    int n = 0;
    for (int i = 0; i < m; i++)
        Thread_new(unsafe, &n, 0);
    Thread_join(NULL);

    printf("test unsafe:%d == %d\n", n, NBUMP*m*3);
}

struct args {
    Sem_T *mutex;
    int *ip;
};

static int
safe(void *arg)
{
    struct args *ar = arg;
    for (int i = 0; i < NBUMP; i++) {
        LOCK(*ar->mutex);
            int n = *ar->ip;
            n = n + 1;
            Thread_pause(); // Simulate preempting
            n = n + 1;
            Thread_pause();
            *ar->ip = n + 1;
        END_LOCK;
    }
    return 0;
}

static void
test_safe(int m)
{
    int n = 0;
    Sem_T mutex;
    Sem_init(&mutex, 1);

    struct args ar; 
    ar.mutex = &mutex;
    ar.ip = &n;

    for (int i = 0; i < m; i++)
        Thread_new(safe, &ar, (int)sizeof(ar));
    Thread_join(NULL);

    printf("  test safe:%d == %d\n", n, NBUMP*m*3);
}

int main(int argc, char **argv)
{
    int m = 5;
    if (argc >= 2)
        m = atoi(argv[1]);
    assert(m > 0);
    printf("Usage: %s [%d] [flag]\n", argv[0], m);
    Thread_init(0);

    if (argc >= 3)
        test_safe(m);
    else
        test_unsafe(m);

    Thread_exit(0);
    return 0;
}

